﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.Text.RegularExpressions;
using System.Collections;
using System.Windows.Data;

namespace SLDataGrid
{
    public partial class Page : UserControl
       
        {
            string Columns;
            string Data;
            public Page(string Columns, string Data)
            {
                InitializeComponent();
                // Initializing Columns and Data in the Code Behind
                // Columns Format : ColumnName1§ColumnName2§ColumnName3
                // See App.xaml.cs for details
                this.Columns = Columns;
                this.Data = Data;
                //this.Columns = "Col1§Col2§Col3§";
                //this.Data = "data1§data2§data3£data4§data5§data6£data7§data8§data9";

                foreach (string SingleColumn in this.Columns.Split('§'))
                    {
                        if (SingleColumn != "")
                        {
                            DataGridTextColumn textColumn = new DataGridTextColumn();
                            textColumn.Header = SingleColumn;
                            textColumn.Binding = new Binding(SingleColumn);
                            SLGrid.Columns.Add(textColumn);
                        }
                    }

                SLGrid.ItemsSource = GenerateData().ToDataSource();
            }


            // Set the data from the Datas string
            // Could be XML deserialisation or string parsing or any other way
            public IEnumerable<IDictionary> GenerateData()
            {
                string[] dataSplit = this.Data.Split('£');
                string[] ColumnSplit = this.Columns.Split('§');
                int ColumnNumber = ColumnSplit.Count();
                var dict = new Dictionary<string, object>();
                int RowNumber = dataSplit[0].Split('§').Count();
                int row = RowNumber;
                for (var i = 0; i < RowNumber; i++)
                {
                    for (var j = 0; j < ColumnNumber - 1; j++)
                    {
                        dict[ColumnSplit[j]] = dataSplit[j].Split('§')[i];
                    }
                    yield return dict;
                }
            }
        }

    public static class DataSourceCreator
    {
        private static readonly Regex PropertNameRegex =
                new Regex(@"^[A-Za-z]+[A-Za-z0-9_]*$", RegexOptions.Singleline);

        public static IEnumerable ToDataSource(this IEnumerable<IDictionary> list)
        {
            IDictionary firstDict = null;
            bool hasData = false;
            foreach (IDictionary currentDict in list)
            {
                hasData = true;
                firstDict = currentDict;
                break;
            }
            if (!hasData)
            {
                return new object[] { };
            }
            if (firstDict == null)
            {
                throw new ArgumentException("IDictionary entry cannot be null");
            }

            Type objectType = null;

            TypeBuilder tb = GetTypeBuilder(list.GetHashCode());

            ConstructorBuilder constructor =
                        tb.DefineDefaultConstructor(
                                    MethodAttributes.Public |
                                    MethodAttributes.SpecialName |
                                    MethodAttributes.RTSpecialName);

            foreach (DictionaryEntry pair in firstDict)
            {
                if (PropertNameRegex.IsMatch(Convert.ToString(pair.Key), 0))
                {
                    CreateProperty(tb,
                                    Convert.ToString(pair.Key),
                                    pair.Value == null ?
                                                typeof(object) :
                                                pair.Value.GetType());
                }
                else
                {
                    throw new ArgumentException(
                                @"Each key of IDictionary must be 
                                alphanumeric and start with character.");
                }
            }
            objectType = tb.CreateType();

            return GenerateEnumerable(objectType, list, firstDict);
        }

        private static IEnumerable GenerateEnumerable(
                 Type objectType, IEnumerable<IDictionary> list, IDictionary firstDict)
        {
            var listType = typeof(List<>).MakeGenericType(new[] { objectType });
            var listOfCustom = Activator.CreateInstance(listType);

            foreach (var currentDict in list)
            {
                if (currentDict == null)
                {
                    throw new ArgumentException("IDictionary entry cannot be null");
                }
                var row = Activator.CreateInstance(objectType);
                foreach (DictionaryEntry pair in firstDict)
                {
                    if (currentDict.Contains(pair.Key))
                    {
                        PropertyInfo property =
                            objectType.GetProperty(Convert.ToString(pair.Key));
                        property.SetValue(
                            row,
                            Convert.ChangeType(
                                    currentDict[pair.Key],
                                    property.PropertyType,
                                    null),
                            null);
                    }
                }
                listType.GetMethod("Add").Invoke(listOfCustom, new[] { row });
            }
            return listOfCustom as IEnumerable;
        }

        private static TypeBuilder GetTypeBuilder(int code)
        {
            AssemblyName an = new AssemblyName("TempAssembly" + code);
            AssemblyBuilder assemblyBuilder =
                AppDomain.CurrentDomain.DefineDynamicAssembly(
                    an, AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");

            TypeBuilder tb = moduleBuilder.DefineType("TempType" + code
                                , TypeAttributes.Public |
                                TypeAttributes.Class |
                                TypeAttributes.AutoClass |
                                TypeAttributes.AnsiClass |
                                TypeAttributes.BeforeFieldInit |
                                TypeAttributes.AutoLayout
                                , typeof(object));
            return tb;
        }

        private static void CreateProperty(
                        TypeBuilder tb, string propertyName, Type propertyType)
        {
            FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName,
                                                        propertyType,
                                                        FieldAttributes.Private);


            PropertyBuilder propertyBuilder =
                tb.DefineProperty(
                    propertyName, PropertyAttributes.HasDefault, propertyType, null);
            MethodBuilder getPropMthdBldr =
                tb.DefineMethod("get_" + propertyName,
                    MethodAttributes.Public |
                    MethodAttributes.SpecialName |
                    MethodAttributes.HideBySig,
                    propertyType, Type.EmptyTypes);

            ILGenerator getIL = getPropMthdBldr.GetILGenerator();

            getIL.Emit(OpCodes.Ldarg_0);
            getIL.Emit(OpCodes.Ldfld, fieldBuilder);
            getIL.Emit(OpCodes.Ret);

            MethodBuilder setPropMthdBldr =
                tb.DefineMethod("set_" + propertyName,
                  MethodAttributes.Public |
                  MethodAttributes.SpecialName |
                  MethodAttributes.HideBySig,
                  null, new Type[] { propertyType });

            ILGenerator setIL = setPropMthdBldr.GetILGenerator();

            setIL.Emit(OpCodes.Ldarg_0);
            setIL.Emit(OpCodes.Ldarg_1);
            setIL.Emit(OpCodes.Stfld, fieldBuilder);
            setIL.Emit(OpCodes.Ret);

            propertyBuilder.SetGetMethod(getPropMthdBldr);
            propertyBuilder.SetSetMethod(setPropMthdBldr);
        }
    }
}
